/**	
 * タイトル：	JDBC代理类
 * 説明：    	
 *			
 * 会社名：	紹興日創軟件有限公司
 * 変更履歴：	2014/01/13 NIS 何
 *			新規作成
 *
 */
package com.nissoft.framework.database;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.jdbc.core.ArgumentPreparedStatementSetter;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.ColumnMapRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.RowMapperResultSetExtractor;
import org.springframework.jdbc.core.SingleColumnRowMapper;
import org.springframework.jdbc.core.SqlProvider;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.util.Assert;

import com.nissoft.framework.exception.DataSearchException;
import com.nissoft.framework.exception.DataUpdateException;
import com.nissoft.framework.message.MessageReader;

/**
 * @author NIS 何
 * @version 1.0
 */
public class JdbcProxy {

	protected final Log logger = LogFactory.getLog(getClass());
	private MessageReader reader;
	private JdbcTemplate jdbcTemplate;

	public JdbcTemplate getJdbcTemplate() {
		return jdbcTemplate;
	}

	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}

	public MessageReader getReader() {
		return reader;
	}

	public void setReader(MessageReader reader) {
		this.reader = reader;
	}

	/**
	 * 使用Statement查询
	 * 
	 * @param sql
	 * @param rse
	 * @return 查询结果
	 */
	public <T> T query(String sql, ResultSetExtractor<T> rse) {
		try {
			return jdbcTemplate.query(resolveSQL(sql), rse);
		} catch (DataAccessException e) {
			// TODO
			throw new DataSearchException(e.getMessage(), e);
		}
	}

	public <T> List<T> query(String sql, RowMapper<T> rowMapper) {
		return query(sql, new RowMapperResultSetExtractor<T>(rowMapper));
	}

	public <T> T queryForObject(String sql, RowMapper<T> rowMapper) {
		List<T> results = query(sql, rowMapper);
		return requiredSingleResult(results);
	}

	public <T> T queryForObject(String sql, Class<T> requiredType) {
		return queryForObject(sql, new SingleColumnRowMapper<T>(requiredType));
	}

	public Map<String, Object> queryForMap(String sql) {
		return queryForObject(sql, new ColumnMapRowMapper());
	}

	public <T> List<T> queryForList(String sql, Class<T> elementType) {
		return query(sql, new SingleColumnRowMapper<T>(elementType));
	}

	public List<Map<String, Object>> queryForList(String sql) {
		return query(sql, new ColumnMapRowMapper());
	}

	/**
	 * 使用PreparedStatement查询
	 * 
	 * @param psc
	 * @param pss
	 * @param rse
	 * @return 查询结果
	 */
	public <T> T query(PreparedStatementCreator psc,
			PreparedStatementSetter pss, ResultSetExtractor<T> rse) {
		try {
			return jdbcTemplate.query(psc, pss, rse);
		} catch (DataAccessException e) {
			// TODO
			throw new DataSearchException(e.getMessage(), e);
		}
	}

	public <T> T query(PreparedStatementCreator psc, ResultSetExtractor<T> rse) {
		return query(psc, null, rse);
	}

	public <T> List<T> query(PreparedStatementCreator psc,
			RowMapper<T> rowMapper) {
		return query(psc, new RowMapperResultSetExtractor<T>(rowMapper));
	}

	public <T> T query(String sql, PreparedStatementSetter pss,
			ResultSetExtractor<T> rse) {
		return query(new SimplePreparedStatementCreator(resolveSQL(sql)), pss,
				rse);
	}

	public <T> T query(String sql, Object[] args, ResultSetExtractor<T> rse) {
		return query(sql, new ArgumentPreparedStatementSetter(args), rse);
	}

	public <T> T query(String sql, ResultSetExtractor<T> rse, Object... args) {
		return query(sql, new ArgumentPreparedStatementSetter(args), rse);
	}

	public <T> List<T> query(String sql, PreparedStatementSetter pss,
			RowMapper<T> rowMapper) {
		return query(sql, pss, new RowMapperResultSetExtractor<T>(rowMapper));
	}

	public <T> List<T> query(String sql, Object[] args, RowMapper<T> rowMapper) {
		return query(sql, args, new RowMapperResultSetExtractor<T>(rowMapper));
	}

	public <T> List<T> query(String sql, RowMapper<T> rowMapper, Object... args) {
		return query(sql, args, new RowMapperResultSetExtractor<T>(rowMapper));
	}

	public <T> T queryForObject(String sql, RowMapper<T> rowMapper,
			Object... args) {
		List<T> results = query(sql, args, new RowMapperResultSetExtractor<T>(
				rowMapper, 1));
		return requiredSingleResult(results);
	}

	public <T> T queryForObject(String sql, Object[] args,
			RowMapper<T> rowMapper) {
		List<T> results = query(sql, args, new RowMapperResultSetExtractor<T>(
				rowMapper, 1));
		return requiredSingleResult(results);
	}

	public <T> T queryForObject(String sql, Class<T> requiredType,
			Object... args) {
		return queryForObject(sql, args, new SingleColumnRowMapper<T>(
				requiredType));
	}

	public Map<String, Object> queryForMap(String sql, Object... args) {
		return queryForObject(sql, args, new ColumnMapRowMapper());
	}

	public <T> List<T> queryForList(String sql, Object[] args,
			Class<T> elementType) {
		return query(sql, args, new SingleColumnRowMapper<T>(elementType));
	}

	public <T> List<T> queryForList(String sql, Class<T> elementType,
			Object... args) {
		return query(sql, args, new SingleColumnRowMapper<T>(elementType));
	}

	public List<Map<String, Object>> queryForList(String sql, Object... args) {
		return query(sql, args, new ColumnMapRowMapper());
	}

	/**
	 * 更新
	 * 
	 * @param sql
	 * @param pss
	 * @return 影响的行数
	 */
	public int update(String sql, PreparedStatementSetter pss) {
		try {
			return jdbcTemplate.update(resolveSQL(sql), pss);
		} catch (DataAccessException e) {
			// TODO
			throw new DataUpdateException(e.getMessage(), e);
		}
	}

	public int update(String sql, Object... args) {
		return update(sql, new ArgumentPreparedStatementSetter(args));
	}

	/**
	 * 更新
	 * 
	 * @param psc
	 * @param generatedKeyHolder
	 *            插入时自动生成的主键
	 * @return 影响行数
	 */
	public int update(PreparedStatementCreator psc, KeyHolder generatedKeyHolder) {
		try {
			return jdbcTemplate.update(psc, generatedKeyHolder);
		} catch (DataAccessException e) {
			// TODO
			throw new DataUpdateException(e.getMessage(), e);
		}
	}

	/**
	 * 批量更新
	 * 
	 * @param sql
	 * @param pss
	 * @return 影响行数
	 */
	public int[] batchUpdate(String sql, BatchPreparedStatementSetter pss) {
		try {
			return jdbcTemplate.batchUpdate(resolveSQL(sql), pss);
		} catch (DataAccessException e) {
			// TODO
			throw new DataUpdateException(e.getMessage(), e);
		}
	}

	/**
	 * 批量更新
	 * 
	 * @param sql
	 * @param batchArgs
	 * @return 影响行数
	 */
	public int[] batchUpdate(String sql, List<Object[]> batchArgs) {
		return jdbcTemplate.batchUpdate(resolveSQL(sql), batchArgs);
	}

	/**
	 * SQL文解析
	 */
	public String resolveSQL(String sql) {
		try {
			sql = reader.read(sql);
			logger.debug("resolve sql:[" + sql + "]");
			return sql;
		} catch (Exception e) {
			throw new InvalidDataAccessResourceUsageException(e.getMessage(), e);
		}
	}

	// private static <T> T requiredSingleResult(List<T> results) {
	// return DataAccessUtils.requiredSingleResult(results);
	// }

	private static <T> T requiredSingleResult(List<T> results) {
		return results.size() > 0 ? results.get(0) : null;
	}

	private static class SimplePreparedStatementCreator implements
			PreparedStatementCreator, SqlProvider {

		private final String sql;

		public SimplePreparedStatementCreator(String sql) {
			Assert.notNull(sql, "SQL must not be null");
			this.sql = sql;
		}

		@Override
		public PreparedStatement createPreparedStatement(Connection con)
				throws SQLException {
			return con.prepareStatement(this.sql);
		}

		@Override
		public String getSql() {
			return this.sql;
		}
	}
}
